package com.webex.ta.hydra.gui.autocomplete;

import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Iterator;
import java.util.Vector;

/**
 * Created by IntelliJ IDEA.
 * User: vegaz
 * Date: 2010-8-16
 * Time: 13:21:12
 * To change this template use File | Settings | File Templates.
 */
public class AutoCompleteComboBox extends JComboBox {

    //Store all the items here
    private Vector allItems = new Vector();

    public AutoCompleteComboBox(Vector items) {
        super(items);
        // Save another copy of items to allItems
        // because the items in ComboBoxModel change with user typing
//        allItems = (Vector)items.clone();
        for (Iterator iter = items.iterator(); iter.hasNext();) {
            allItems.add(iter.next());
        }
        this.setSelectedIndex(-1);
        this.setUI(new AutoCompleteComboBoxUI());
        JTextField editor = (JTextField) this.getEditor().getEditorComponent();
        final KeyListener keyListener = new KeyAdapter() {
            public void keyPressed(KeyEvent keyEvent) {
                // don't popup on action keys (cursor movements, etc...)
                if (keyEvent.isActionKey()) return;
                // don't popup if the combobox isn't visible anyway
                if (isDisplayable() && !isPopupVisible()) {
                    int keyCode = keyEvent.getKeyCode();
                    // don't popup when the user hits shift,ctrl or alt
                    if (keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_ALT)
                        return;
                    setPopupVisible(true);
                }
            }
        };
        editor.addKeyListener(keyListener);
        editor.setDocument(new Document(this));
    }


//    public void setSelectedItem(Object anObject) {
//	Object oldSelection = selectedItemReminder;
//        Object objectToSelect = anObject;
//	if (oldSelection == null || !oldSelection.equals(anObject)) {
//
//	    if (anObject != null && !isEditable()) {
//		// For non editable combo boxes, an invalid selection
//		// will be rejected.
//		boolean found = false;
//		for (int i = 0; i < dataModel.getSize(); i++) {
//                    Object element = dataModel.getElementAt(i);
//		    if (anObject.equals(element)) {
//			found = true;
//                        objectToSelect = element;
//			break;
//		    }
//		}
//		if (!found) {
//		    return;
//		}
//	    }
//
//	    // Must toggle the state of this flag since this method
//	    // call may result in ListDataEvents being fired.
////	    selectingItem = true;
//	    dataModel.setSelectedItem(objectToSelect);
////	    selectingItem = false;
//
//	    if (selectedItemReminder != dataModel.getSelectedItem()) {
//		// in case a users implementation of ComboBoxModel
//		// doesn't fire a ListDataEvent when the selection
//		// changes.
//		selectedItemChanged();
//	    }
//	}
////	fireActionEvent();
//    }


    public void notifyUI() {
        if (getUI() instanceof AutoCompleteComboBoxUI && isEditable()) {
            AutoCompleteComboBoxUI ui = (AutoCompleteComboBoxUI) getUI();
            ui.updatePopupList(lookupItems(editor.getItem().toString()));
        }
    }

    private Object[] lookupItems(String pattern) {
        Vector<Object> results = new Vector<Object>();

        // iterate over all items
        for (Object currentItem : allItems) {
            // current item starts with the pattern?
            if (currentItem != null && startsWithIgnoreCase(currentItem.toString(), pattern)) {
                results.add(currentItem);
            }
        }
        return results.toArray();
    }

    private static boolean startsWithIgnoreCase(String string1, String string2) {
        // this could be optimized, but anyway it doesn't seem to be a performance killer
        return string1.toUpperCase().startsWith(string2.toUpperCase());
    }

    private class Document extends PlainDocument {

        private AutoCompleteComboBox selector;

        private boolean selecting = false;

        public Document(AutoCompleteComboBox combo) {
            super();
            this.selector = combo;
        }

//        @Override
//        public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
//            if (selecting) return;
//            super.replace(offset, length, text, attrs);    //To change body of overridden methods use File | Settings | File Templates.
//            notifyUI();
//        }

        public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
            // return immediately when selecting an item
            if (selecting) return;
//            System.out.println("insertString:" + offs + "," + str);
            // insert the string into the document
            super.insertString(offs, str, a);

            // lookup and select a matching item
//            Object item = lookupItem(getText(0, getLength()));
//            if (item != null) {
//                setSelectedItem(item);
//            } else {
//                // no item matches => use the current input as selected item
//                item = getText(0, getLength());
//                setSelectedItem(item);
//            }
//            setText(item == null ? "" : item.toString());
            // select the completed part
//            ((JTextComponent) selector.getEditor().getEditorComponent()).setCaretPosition(((JTextComponent) selector.getEditor().getEditorComponent()).getText().length());
//            ((JTextComponent) selector.getEditor().getEditorComponent()).moveCaretPosition(offs + str.length());
            notifyUI();
        }

        public void remove(int offs, int len) throws BadLocationException {

            if (selecting) return;
//            System.out.println("remove:" + offs + "," + len);
            super.remove(offs, len);
//            setSelectedItem(getText(0, getLength()));
//            Object item = lookupItem(getText(0, getLength()));
//            setSelectedItem(item);
//            ((JTextComponent) selector.getEditor().getEditorComponent()).setCaretPosition(offs);
            notifyUI();
        }

        private void setSelectedItem(Object item) {
            selecting = true;
            selector.setSelectedItem(item);
            selecting = false;
        }

        private Object lookupItem(String pattern) {
            Object selectedItem = selector.getSelectedItem();
            // only search for a different item if the currently selected does not match
            if (selectedItem != null && startsWithIgnoreCase(selectedItem.toString(), pattern)) {
                return selectedItem;
            } else {
                // iterate over all items
                for (int i = 0, n = selector.getItemCount(); i < n; i++) {
                    Object currentItem = selector.getItemAt(i);
                    // current item starts with the pattern?
                    if (currentItem != null && startsWithIgnoreCase(currentItem.toString(), pattern)) {
                        return currentItem;
                    }
                }
            }
            // no item starts with the pattern => return null
            return null;
        }


        private void setText(String text) {
            try {
                // remove all text and insert the completed string
                super.remove(0, getLength());
                super.insertString(0, text, null);
            } catch (BadLocationException e) {
                throw new RuntimeException(e.toString());
            }
        }
    }

    public void actionPerformed(ActionEvent e) {
        Object newItem = getEditor().getItem();
        setPopupVisible(false);
        getModel().setSelectedItem(newItem);
	String oldCommand = getActionCommand();
	setActionCommand("comboBoxEdited");
	fireActionEvent();
	setActionCommand(oldCommand);
    }
}
